<?php
namespace app\common\logic;

use app\admin\logic\system\Config as SystemConfig;
use app\common\exception\LogicException;
use app\common\model\Verify as VerifyModel;
use eduline\sms\Client;
use think\helper\Str;
use think\facade\Env;

/**
 * 验证码
 */
class Verify extends VerifyModel
{

    public function send($data): bool
    {
        // 验证数据
        $this->getValidate()->scene('send')->failException()->check($data);
        $type  = $data['type'];
        $phone = strval($data['phone']);

        // 配置参数
        $verifyConf = SystemConfig::get('system.package.sms.verify', ['length' => 6], request()->mhm_id);
        $length     = $verifyConf['length'];
        $valid      = 5 * 60;// 5分钟有效期
        $time       = time();
        $cache_term = 'verify_time_' . $type . '_' . $phone;
        $cache_str  = 'verify_' . $type . '_' . $phone;

        // 检查重复发送，60秒
        if (cache($cache_term)) {
            throw new LogicException('请勿重复发送');
        }

        // 当前操作的手机号的验证码数据库信息
        $verify = $this->where(['phone' => $phone, 'type' => $type])->findOrEmpty();

        // 当前操作的手机号的验证码缓存信息
        $cache  = cache($cache_str);

        // 操作
        if ($verify->isEmpty()) {
            // 存在缓存使用缓存
            $code = $cache ?: Str::random($length, 1);
            $save = ['phone' => $phone, 'type' => $type, 'code' => $code, 'update_time' => $time];
            $re   = $this->save($save);
            // 存入数据库成功后删除缓存，否则存入缓存
            if (!$re) {
                cache($cache_str, $code, $valid);
            } else {
                cache($cache_str, null);
            }
        } else if ($verify->update_time + $valid > $time) {
            // 数据库有数据且未过期，使用数据库code
            $code = $verify->code;
        } else {
            // 数据库有数据且已过期，重新发送
            $code                = Str::random($length, 1);
            $verify->code        = $code;
            $verify->update_time = $time;
            $re                  = $verify->save();
            // 存入数据库成功后删除缓存，否则存入缓存
            if (!$re) {
                cache($cache_str, $code, $valid);
            } else {
                cache($cache_str, null);
            }
        }

        // 短信发送失败
        if (!Client::send($phone, ['code' => $code])) {
            throw new LogicException('发送失败');
        }

        // 重复发送，60秒
        cache($cache_term, 1, 60);
        return true;
    }

    public function check($phone, $type, $verify)
    {
        // 验证数据
        $param = ['phone' => $phone, 'type' => $type, 'verify' => $verify];
        $this->getValidate()->scene('check')->failException()->check($param);

        // 调试模式使用
        if ( Env::get('app_debug') && $verify == 123456) return;

        $code = $this
            ->where('phone', $phone)
            ->where('type', $type)
            ->where('update_time', '>', time()-5*60)
            ->value('code');

        if (!$code) {
            $code = cache('verify_' . $type . '_' . $phone);
            if ($code) {
                $save = ['phone' => $phone, 'type' => $type, 'code' => $code, 'update_time' => time()];
                $re   = app(Verify::class)->save($save);
                // 存入数据失败，修改code为null
                if (!$re) {
                    $code = null;
                }
            }
        }

        if (intval($verify) !== intval($code)) {
            throw new LogicException('短信验证码错误');
        }
    }
}
